/**
 * Tests that a failing index build on a secondary node which is trying to signal the primary, is
 * properly interrupted, without blocking shutdown, and restarted after shutdown.
 *
 * @tags: [
 *   requires_fcv_71,
 *   requires_replication,
 * ]
 */
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {IndexBuildTest} from "jstests/noPassthrough/libs/index_build.js";

const rst = new ReplSetTest({
    nodes: [
        {},
        {
            // Disallow elections on secondary.
            rsConfig: {
                priority: 0,
            },
        },
    ]
});
rst.startSet();
rst.initiate();

const primary = rst.getPrimary();

const testDB = primary.getDB('test');
const coll = testDB.getCollection('test');

const secondary = rst.getSecondary();
const secondaryDB = secondary.getDB(testDB.getName());
let secondaryColl = secondaryDB.getCollection('test');

// Avoid optimization on empty colls.
assert.commandWorked(coll.insert({a: 1}));

// Pause the index builds on the secondary, using the 'hangAfterStartingIndexBuild' failpoint.
const failpointHangAfterInit = configureFailPoint(secondaryDB, "hangAfterInitializingIndexBuild");

// Create the index and start the build. Set commitQuorum of 2 nodes explicitly, otherwise as only
// primary is voter, it would immediately commit.
const createIdx = IndexBuildTest.startIndexBuild(primary,
                                                 coll.getFullName(),
                                                 {a: 1},
                                                 {},
                                                 [ErrorCodes.InterruptedDueToReplStateChange],
                                                 /*commitQuorum: */ 2);

failpointHangAfterInit.wait();

// Extract the index build UUID. Use assertIndexesSoon to retry until the oplog applier is done with
// the entry, and the index is visible to listIndexes. The failpoint does not ensure this.
const buildUUID =
    IndexBuildTest
        .assertIndexesSoon(secondaryColl, 2, ['_id_'], ['a_1'], {includeBuildUUIDs: true})['a_1']
        .buildUUID;

const hangBeforePrimarySignal =
    configureFailPoint(secondaryDB, "hangIndexBuildBeforeSignalingPrimaryForAbort");
const failSecondaryBuild =
    configureFailPoint(secondaryDB,
                       "failIndexBuildWithError",
                       {buildUUID: buildUUID, error: ErrorCodes.OutOfDiskSpace});

// Unblock index builds, causing the failIndexBuildWithError failpoint to throw an error.
failpointHangAfterInit.off();
hangBeforePrimarySignal.wait();

// Restarting the secondary while voting causes it to be interrupted, even then it should mark the
// build as aborted for resume and allow shutdown to finish.
rst.restart(secondary);
rst.waitForPrimary();

// Should exit normally, because after restart the error failpoint is not set. The index build
// should be restarted, and vote commit to the primary. InterruptedDueToReplStateChange is ignored,
// as a slow secondary restart can cause the primary to step down due to heartbeat timeout.
createIdx();

secondaryColl = rst.getSecondary().getDB(testDB.getName()).getCollection('test');

// Wait for index build to finish.
IndexBuildTest.waitForIndexBuildToStop(testDB);

// Assert index is committed.
IndexBuildTest.assertIndexes(coll, 2, ['_id_', 'a_1'], []);
IndexBuildTest.assertIndexes(secondaryColl, 2, ['_id_', 'a_1'], []);

rst.stopSet();